<?php
define("WARNING_TITLE", "微信告警通知");
define("WARNING_CONTENT", "您的微信公众帐号【%s】中发生错误\n错误描述为：%s\n错误详情为：%s");
define("FEEDBACK_REQUEST", "request");
define("FEEDBACK_CONFIRM", "confirm");
define("FEEDBACK_REJECT", "reject");
define("FEEDBACK_UPDATED", "updated");
define("TRADE_TYPE_JSAPI", "JSAPI");


// include_once("../wxpay/WxPayPubHelper.class.php");

class WxpayAction extends Action{
    // Native原生支付支付回调URL
    // http://localhost/weixin/wxpay/getpackage.html
    public function getpackage() {
        Output::instance()->resultCode();
    }

    /**
     * 维权通知URL
     * http://localhost/weixin/wxpay/feedback.html
     *
     * @param int $mediaId  自媒体id,170714 - 改多账号底层时修改, 但并没有实际用途
     */
    public function feedback($mediaId = 0) {
        Session::switchCustomer(0);

        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
        Log::write('feedback:' . $postStr);
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);

        $appId = $postObj->AppId;
        $fromUserName = strval($postObj->OpenId);
        $timeStamp = strval($postObj->TimeStamp);
        $dt = date("Y-m-d H:i:s", $timeStamp);

        $msgType = strval($postObj->MsgType);
        $feedbackId = strval($postObj->FeedBackId);
        $reason = strval($postObj->Reason);
        $appSignature = $postObj->AppSignature;
        $signMethod = $postObj->SignMethod;

        /**
         * LLyeap_wx_customer修改
         */
        /***** LLyeap_delete *****/
//        $CustomerModel = M("customer");
//        $customer = $CustomerModel->where("appid='$appId'")->find();
        /*****  *****/

        $IndMediaInfo_Model = M('ind_media_info');
        $customer = $IndMediaInfo_Model->where(['appid' => $appId])->find();
        if ($customer) {
//            $customerId = $customer["id"];
            $customerId = $customer["cid"];

            Session::switchCustomer($customerId);
            // TODO: 签名检查
            $isAccess = $this->checkSignFeedBack($appId, $timeStamp, $fromUserName, $appSignature);
            if ($isAccess) {
                Log::write('submit sign ok');
            }

            $data["msgType"] = $msgType;
            $data["reason"] = $reason;
            $data["updateTime"] = $dt;

            if (isset($postObj->TransId)) {
                $data["wxTransactionId"] = strval($postObj->TransId);
            }
            if (isset($postObj->Solution)) {
                $data["solution"]  = strval($postObj->Solution);
            }
            if (isset($postObj->Solution)) {
                $data["extInfo"]  =  strval($postObj->ExtInfo);
            }
            if (isset($postObj->PicInfo)) {
                $picInfo = $postObj->PicInfo;
                $i = 1;
                foreach($picInfo->item as $item) {
                    $field = "picUrl" . $i;
                    $data[$field] = strval($item->PicUrl) == '' ? NULL : strval($item->PicUrl);
                    $i++;
                }
            }

            $FeedBackModel = M("module_product_order_feedback");

            $where["customerId"] = $customerId;
            $where["fromUserName"] = $fromUserName;
            $where["wxFeedbackId"] = $feedbackId;
            $feedback = $FeedBackModel->where($where)->find();
            if (!$feedback) {
                $data["customerId"] = $customerId;
                $data["fromUserName"] = $fromUserName;
                $data["wxFeedbackId"] = $feedbackId;
                $data["createTime"] = $dt;

                $result = $FeedBackModel->add($data);
            } else {
                $result = $FeedBackModel->where($where)->save($data);
            }
            if (!$result) {
                Log::write("Wxpay feedback mysql error:" . $FeedBackModel->getLastSql());
            }
        } else {
            Log::write("Wxpay feedback no found customer, appid is : " . $appId);
        }
    }

    /**
     * 告警通知URL
     * http://localhost/weixin/wxpay/warning.html
     *
     * @param int $mediaId  自媒体id,170714 - 改多账号底层时修改, 但并没有实际用途
     */
    public function warning($mediaId = 0) {
        Session::switchCustomer(0);

        $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
        Log::write("warning is:" . $postStr);
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);

        //TODO:check sign
        $appId = $postObj->AppId;
        $errorType = intval($postObj->ErrorType);
        $desc = strval($postObj->Description);
        $content = strval($postObj->AlarmContent);
        $timeStamp = strval($postObj->TimeStamp);

        /***** LLyeap_delete *****/
//        $CustomerModel = M("customer");
//        $where["appid"] = $appId;
//        $customerUser = $CustomerModel->where($where)
//            ->join("wx_customer_user user ON user.customerId = wx_customer.id")->field("user.*")->find();
        /*****  *****/

        $IndMediaInfo_Model = M('ind_media_info');
        $where['appid'] = $appId;
        $customerUser = $IndMediaInfo_Model
            ->alias('imi')
            ->where($where)
            ->join("wx_customer_user user ON user.customerId = imi.cid")
            ->field("user.*")
            ->find();

        if ($customerUser ) {
            $customerId = $customerUser['customerId'];

            Session::switchCustomer($customerId);
            $WarningModel = M("module_product_order_warning");
            $data["customerId"] = $customerUser["customerId"];
            $data["errorType"] = $errorType;
            $data["description"] = $desc;
            $data["alarmContent"] = $content;
            $data["createTime"] = date("Y-m-d H:i:s", $timeStamp);
            $WarningModel->add($data);

            $message = sprintf(WARNING_CONTENT, $customerUser["fullName"], $desc, $content);

            $this->sendNoticeWarning(WARNING_TITLE, $message);
// 			if (!empty($customerUser["email"])) {
// 				SendMail($customerUser["email"], WARNING_TITLE, $message);
// 			}
        } else {
            Log::write('Wxpay warning:' . $desc . ',' . $content);
        }

        echo "success";
    }

    public function getFeedBack($fullName = NULL, $msgType = NULL, $startTime='', $endTime='',$order = "fb.updateTime, fb.createTime", $orderBy = "desc", $start = 0, $limit = 25, $isExport = false) {
        $retVal = Session::init();
        if (!$retVal) {
            Output::instance()->resultCode(code_access_denied);
            return;
        }
        $customerId = Session::$user["customerId"];
        Session::switchCustomer(0);
        $FeedBackModel = M("module_product_order_feedback");

        Session::switchCustomer(customerId);
        $where['pod.isF2B'] = 0;

        if ($startTime != '' && $endTime != '') {
            $where['fb.createTime'] = array(array('egt',$startTime),array('elt',$endTime));
        } else if ($startTime != '') {
            $where["fb.createTime"] = array('egt', $startTime);
        } else if ($endTime != '') {
            $where["fb.createTime"] = array('elt', $endTime);
        }

        if($msgType != NULL) $where['fb.msgType'] = $msgType;
        if($fullName != NULL) $where['pod.fullName'] = array('like',"%{$fullName}%");

        if (!$isExport) {
            $arr = $FeedBackModel->alias("fb")
                ->where($where)
                ->join("wx_module_product_order pod ON pod.wxTransactionId = fb.wxTransactionId")
                ->field("fb.*, pod.id as oid, pod.serialNumber, pod.price,pod.fullName, pod.createTime as pTime,
					pod.status, pod.deliveryType, pod.payType, pod.remark, pod.sellerRemark, pod.sellerNote")
                ->order("$order $orderBy")->limit("$start, $limit")->select();
            if ($arr) {
                $count = $FeedBackModel->alias("fb")
                    ->where($where)
                    ->join("wx_module_product_order pod ON pod.wxTransactionId = fb.wxTransactionId")
                    ->field("fb.*, pod.id as oid, pod.serialNumber, pod.price,pod.fullName, pod.createTime as pTime,
						pod.status, pod.deliveryType, pod.payType, pod.remark, pod.sellerRemark, pod.sellerNote")
                    ->count();
                Output::instance()->objArray($arr, $count);
            } else {
                Output::instance()->resultCode();
            }
        }else{
            $arr = $FeedBackModel->alias("fb")
                ->where($where)
                ->join("wx_module_product_order pod ON pod.wxTransactionId = fb.wxTransactionId")
                ->field("fb.*, pod.id as oid, pod.serialNumber, pod.price,pod.fullName, pod.createTime as pTime,
					pod.status, pod.deliveryType, pod.payType, pod.remark, pod.sellerRemark, pod.sellerNote")
			->order("$order $orderBy")->select();
			
			$arr = R('Product/getOrderFeedbackName', $arr);
			$expCellName  = array(
				array('fullName','维权用户'),
				array('orderId','订单序列号'),
				array('pTime','订单创建时间'),
				array('reason','用户投诉原因'),
				array('solution','用户希望解决方案'),
				array('msgType','通知类型'),
				array('createTime','投诉提交时间'),
			);
			Output::instance()->objArrayExcel('维权订单管理', $expCellName, $arr);
		}
	}
	
	public function getWarning($order = "createTime", $orderBy = "desc", $start = 0, $limit = 25) {
		$retVal = Session::init();
		if (!$retVal) {
			Output::instance()->resultCode(code_access_denied);
			return;
		}
		$customerId = Session::$user["customerId"];
		Session::switchCustomer(0);
		$WarningModel = M("module_product_order_warning");
		$arr = $WarningModel->order("$order $orderBy")->limit("$start, $limit")->select();
		Session::switchCustomer($customerId);
		if ($arr) {
			$count = $WarningModel->where($where)->count();
			Output::instance()->objArray($arr, $count);
		} else {
			Output::instance()->resultCode();
		}
	}
	
	public function submitWxpay() {
 		Log::write('in submit wxpay');
        echo "success";
		$sn = $_GET['out_trade_no'];
		$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
		if (isset($sn)) {
			$isOld = true;
			
			$partner = $_GET['partner'];
			$transactionId = $_GET['transaction_id'];
		} else if (isset($xml)) {
			$isOld = false;
			
			$wxPayHelper = new WxPayPubHelper();
			$notify = new Notify_pub();
			$notify->saveData($xml);
			
			$mchId = $notify->data['mch_id'];
			$transactionId = $notify->data['transaction_id'];
			$sn = $notify->data['out_trade_no'];
		} else {
			Log::write('not set out_trade_no id or not set post data');
			return;
		}
		Session::switchCustomer(0);
		$CustomerMallSettingModel = D('customer_mall_setting');
		if ($isOld) {
			$condition['partnerId'] = $partner;
		} else {
			$condition['mchId'] = $mchId;
		} 
		$mallSetting = $CustomerMallSettingModel->where($condition)->find();
		if (!$mallSetting) {
			Log::write('not found mall setting:' . $CustomerMallSettingModel->getLastSql());
			return;
		}
		$customerId = $mallSetting["customerId"];
		Session::switchCustomer(0);

		if ($isOld) {
			// TODO: 签名检查
			$isAccess = $this->checkSignSubmitPayOld();
			if (!$isAccess) {
					
			} else {
				Log::write('check sign submit ok');
			}
		} else {
			if($notify->checkSign($mallSetting["payKey"]) == TRUE) {
				if ($notify->data["return_code"] == "FAIL") {
					Log::write("【通信出错】:".$xml);
				}
				elseif($notify->data["result_code"] == "FAIL"){
					Log::write("【通信出错】:".$xml);
				} 
// 				else {
// 					Log::write('sign ok');
// 				}
			} else {
				Log::write('sign error');
				return;
			}
		}
		
		$OrderModel = M("module_product_order");
		$whereOrderSn["tempSerialNumber"] = $sn;
		$oidArr = $OrderModel->where($whereOrderSn)->getField('id' ,true);
		$oid = implode(",", $oidArr);
		if (!$oid) {
			// 兼容之前版本，之前版本传递的微信支付外部订单单号是id，本次修改后改为订单编号
			$oid = $sn;
			$whereOrderId["id"] = $oid;
			$order = $OrderModel->where($whereOrderId)->find();
			if (!$order) {
				Log::write('[Wxpay - submitWxpay] Not Found:' . $OrderModel->getLastSql());
				return;
			}
		}
		Session::switchCustomer($customerId);
		$result = ProductOrder::updateOrderDone($customerId, $oid, $transactionId, true);
	}
	
	public function updateFeedBack($id) {
		$retVal = Session::init();
		if (!$retVal) {
			Output::instance()->resultCode(code_access_denied);
			return;
		}
		$cid = Session::$user["customerId"];
		Session::switchCustomer(0);
		$FeedBackModel = M("module_product_order_feedback");
		$where["wxFeedbackId"] = $id;
		Session::switchCustomer($cid);
		$feedback = $FeedBackModel->where($where)->find();
		if (!$feedback) {
			Output::instance()->resultCode(code_system_error);
			return;
		}
		
		$accessToken = WeixinApi::getAccessToken();
		if (!$accessToken) {
			return;
		}

        $url = 'https://api.weixin.qq.com/payfeedback/update?access_token=' . $accessToken .
            '&openid=' . $feedback["fromUserName"] .'&feedbackid=' . $id;
        Log::write($url);
        $ch = curl_init($url) ;
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true) ; // 获取数据返回
        curl_setopt($ch, CURLOPT_BINARYTRANSFER, true) ; // 在启用 CURLOPT_RETURNTRANSFER 时候将获取数据返回
        $result = curl_exec($ch);
        curl_close($ch);
        Log::write($result);

        $result = json_decode($result);
        if ($result->errcode !== 0) {
            Output::instance()->resultCode(code_product_feedback_fail, $result->errmsg);
        } else {
            $FeedBackModel->where($where)->setField('msgType', FEEDBACK_UPDATED);
            Output::instance()->resultCode();
        }
    }

    /*
     * 获取微信支付参数
     * sn:订单编号，如果需要自动生成订单编号该值传0
     * desc:商品描述显示在微信支付界面中
     * productFee:产品价格
     * transportFee:运费单位分
     * userName:用户openid
     * module:回调模块，如果产品传NULL，按照正常逻辑处理
     * moduleId:回调模块id
     * method:回调方法
     *
     * 修改记录:
     * 1. 与正式平台同步
     *      public function getWxPayParam($sn, $desc, $productFee, $transportFee, $userName,$payMethod, $module = NULL, $moduleId = 0, $method = '', $tradeType= TRADE_TYPE_JSAPI) {
     *      public function getWxPayParam($sn, $desc, $productFee, $transportFee, $userName,$module = NULL, $moduleId = 0, $method = '', $tradeType= TRADE_TYPE_JSAPI) {
     * @modify lilei<LL_strive@163.com> 2017-05-09
     */
     public function getWxPayParam($sn, $desc, $productFee,
                                  $transportFee, $userName,$module = NULL, $moduleId = 0, $method = '', $tradeType= TRADE_TYPE_JSAPI,$scene_info='') {
        //孟慧修改于20180330，用于支持H5支付
        if($tradeType == TRADE_TYPE_JSAPI){
            $uaValid = isMicroMessengerUserAgent();
            if (!$uaValid) {
                Output::instance()->resultCode(code_useragent_invalid);
                return;
            }
            $versionValid = isMicroMessengerVersionHigher5();
            if (!$versionValid) {
                Output::instance()->resultCode(code_useragent_version_invalid);
                return;
            }
        }

        $setting = R("CUser/getMallSetting");
        Log::write('微信支付设置'.json_encode($setting));
        if (!$setting) {
            return;
        }
        $ip = get_client_ip();
        Log::write('微信参数ｉｐ '.$ip);
        $notifyUrl = C('SERVER_HOST') . "Wxpay/submitWxpay.json";
        $totalFee = $productFee + $transportFee;
        if ($sn == 0) {
            $resultOrder = $this->createOrder($userName, $totalFee, $module, $moduleId, $method,$desc);
            if ($resultOrder) {
                $sn = $resultOrder;
            } else {
                Output::instance()->resultCode(code_product_order_create_failed);
                return;
            }
        }
        if(strlen($desc)>127){
            $result = preg_split("//u", $desc, -1, PREG_SPLIT_NO_EMPTY);
            $length = 0;
            $desc = '';
            foreach($result as $key=>$value){
                $length+=strlen($value);
                if($length<=124){
                    $desc.=$value;
                }
            }
            $desc.="...";
        }
         $cid = Session::$user["customerId"];
         Session::switchCustomer(0);
         $OrderModel = M("module_product_order");
         $whereOrder["serialNumber"] = $sn;
         $oidArr = $OrderModel->where($whereOrder)->getField("id", true);
         $oid = implode(",", $oidArr);
        if ($setting["isOld"]) {
            $pkg = $this->createParamOld($desc, $sn, $totalFee, $productFee, $transportFee, $notifyUrl, $ip, $setting);
        } else {
            $pkg = $this->createParam($userName, $desc, $sn, $totalFee, $notifyUrl, $setting, $tradeType,$scene_info,$oid);
        }
        if (!$pkg) {
            return;
        }
         Log::write('微信参数'.json_encode($pkg));
        $result["pkg"] = $pkg;
        $result["id"] = $oid;
        $result["price"] = $totalFee;
        Log::write('微信结果'.json_encode($result));
        $cid = Session::$user["customerId"];
        Session::switchCustomer($cid);
        Output::instance()->object($result);
    }

    private function createParamOld($desc, $sn, $totalFee, $productFee, $transportFee, $notifyUrl, $ip, $setting) {
        $wxPayHelper = new WxPayHelper();
        $wxPayHelper->setParameter("bank_type", "WX");
        $wxPayHelper->setParameter("body", $desc);
        $wxPayHelper->setParameter("partner", $setting["partnerId"]);
        $wxPayHelper->setParameter("out_trade_no", strval($sn));
        $wxPayHelper->setParameter("total_fee", strval($totalFee * 100));
        $wxPayHelper->setParameter("fee_type", "1");
        $wxPayHelper->setParameter("product_fee", strval($productFee * 100));
        $wxPayHelper->setParameter("transport_fee", strval($transportFee * 100));
        $wxPayHelper->setParameter("notify_url", $notifyUrl);
        $wxPayHelper->setParameter("spbill_create_ip", $ip);
        $wxPayHelper->setParameter("input_charset", "UTF-8");

        $pkg = $wxPayHelper->create_biz_package($setting);
        return $pkg;
    }

    private function createParam($userName, $desc, $sn, $totalFee, $notifyUrl, $setting, $tradeType,$scene_info,$oid) {
        // new 基类，否则找不到其继承类

        $wxPayHelper = new WxPayPubHelper();
        //使用统一支付接口
        $unifiedOrder = new UnifiedOrder_pub();
        $appId = $setting['appId'];
        if($setting['merchantMode']){
            $mchId = $setting['merchantMechId'];
            $payKey = $setting['merchantKey'];
            $unifiedOrder->setParameter("sub_mch_id",$setting['subMechId']);
        }else{
            $mchId = $setting['mchId'];
            $payKey = $setting['payKey'];
        }
        $unifiedOrder->setParameter("appid", $appId);
        $unifiedOrder->setParameter("mch_id", $mchId);
        $unifiedOrder->setParameter("openid", $userName);
        $unifiedOrder->setParameter("body", $desc);//商品描述
        $unifiedOrder->setParameter("out_trade_no", strval($sn));//商户订单号
        $unifiedOrder->setParameter("total_fee", strval($totalFee * 100));//总金额
        $unifiedOrder->setParameter("notify_url", $notifyUrl);//通知地址
        $unifiedOrder->setParameter("trade_type", $tradeType);//交易类型
        //非必填参数，商户可根据实际情况选填
        //$unifiedOrder->setParameter("sub_mch_id","1417692302");//子商户号
        //$unifiedOrder->setParameter("device_info","XXXX");//设备号
        //$unifiedOrder->setParameter("attach","XXXX");//附加数据
        //$unifiedOrder->setParameter("time_start","XXXX");//交易起始时间
        //$unifiedOrder->setParameter("time_expire","XXXX");//交易结束时间
        //$unifiedOrder->setParameter("goods_tag","XXXX");//商品标记
        //$unifiedOrder->setParameter("product_id","XXXX");//商品ID
        empty($scene_info)? '' : $unifiedOrder->setParameter("scene_info",$scene_info);//商品ID
        if(isMicroMessengerUserAgent()){
            //判断是否是微信浏览器，微信浏览器的话支付方式和之前一样无变化
            $prepay_id = $unifiedOrder->getPrepayId($payKey);
            Log::write('unifiedOrder'.json_encode($unifiedOrder));
            Log::write('prepay_id'.$prepay_id);

            //使用jsapi接口调起支付
            $jsApi = new JsApi_pub();
            $jsApi->setPrepayId($prepay_id);
            $jsApiParameters = $jsApi->getParameters($appId, $payKey);
            return $jsApiParameters;
        }else{
            $H5info = $unifiedOrder->getH5PayUrl($payKey);
            Log::write('unifiedOrder'.json_encode($unifiedOrder));
            Log::write('prepay_id'.$H5info['prepay_id']);
            $result['code'] = 0;
            $result['mweb_url'] = $H5info['mweb_url'];
            $result['id'] = $oid;
            Output::instance()->object($result);
        }

    }

    /*
     * 微信支付的退款
     *
     */
    public function wxpayRefund($wxTransactionId, $rid, $totalPrice,
                                $refundMoney, $refundPwd, $oid, $sn,$merchantMode=0) {
        $setting = R("CUser/getMallSetting");
        if (!$setting) {
            return;
        }
        $isOld = $setting["isOld"];
        if ($isOld) {
            $partnerId = $setting["partnerId"];
            $key = $setting["partnerKey"];
            $filePem = WXPAY_CERT_PATH . $partnerId . ".pem";
            $filePemCert = WXPAY_CERT_PATH . "cert.pem";
            if (!file_exists($filePem) || !file_exists($filePemCert)) {
                Log::write('refund pem not exisit ' . $filePem . ',' . $filePemCert);
                Output::instance()->resultCode(code_product_order_refund_pem_not_exist);
                return;
            }
        }

        if ($isOld) {
            $result = $this->refundOld($partnerId, $wxTransactionId, $rid, $totalPrice, $refundMoney, $refundPwd,
                $key, $filePem, $filePemCert);
        } else {


            $result = $this->refund($setting, $sn, $rid, $totalPrice, $refundMoney,$merchantMode);
// 			$result = $this->refundOld($mchId, $wxTransactionId, $rid, $totalPrice, $refundMoney, $refundPwd,
// 				 $key, $filePem, $filePemCert);
        }
        if ($result === false) {
            ProductOrder::updateOrderRefund($oid, $rid, false, '');
            return;
        }

        $resultCode = $result["code"];
        $resultMsg = $result["msg"];
        if ($resultCode === 0) {
            R('Product/refundStatistics', array($oid,$refundMoney));
            ProductOrder::updateOrderRefund($oid, $rid, true, $resultMsg);

            Output::instance()->resultCode();
        } else {
            ProductOrder::updateOrderRefund($oid, $rid, false, $resultMsg);

            Output::instance()->resultCode(code_product_order_refund_failed, $resultMsg);
        }
    }

    private function refundOld($partnerId, $wxTransactionId, $rid, $totalPrice, $refundMoney, $refundPwd,
                               $key, $filePem, $filePemCert) {

        $wxPayHelper = new WxPayHelper();
        $wxPayHelper->setParameter("partner", $partnerId);
        $wxPayHelper->setParameter("transaction_id", $wxTransactionId);
        $wxPayHelper->setParameter("out_refund_no", $rid);
        $wxPayHelper->setParameter("total_fee", $totalPrice);
        $wxPayHelper->setParameter("refund_fee", $refundMoney);
        $wxPayHelper->setParameter("op_user_id", $partnerId);
        $wxPayHelper->setParameter("op_user_passwd", $refundPwd);
        $wxPayHelper->setParameter("input_charset", "UTF-8");
        //	$wxPayHelper->setParameter("service_version", "1.1");

        $signObj = $wxPayHelper->signMd5($key);
        $param = $signObj["unsign"] . "&sign=" . $signObj["sign"];
        Log::write('wx refund param: ' . $param);

        $url = 'https://mch.tenpay.com/refundapi/gateway/refund.xml';
        // 		Log::write($url);
        $ch = curl_init($url) ;
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_URL,$url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

        //	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

        // cert
        curl_setopt($ch, CURLOPT_SSLCERT, $filePem);
        curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $partnerId);
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, "PEM");

        // ca
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_CAINFO, $filePemCert);

        $result = curl_exec($ch);
        curl_close($ch);
        Log::write($result);
        if (empty($result)) {
            Output::instance()->resultCode(code_product_order_refund_failed, '退款失败服务器无响应');
            return false;
        }

        $resultXml = simplexml_load_string($result, 'SimpleXMLElement', LIBXML_NOCDATA);
        $resultCode = intval($resultXml->retcode);
        $resultMsg = strval($resultXml->retmsg);

        $refundResult["code"] = $resultCode;
        $refundResult["msg"] = $resultMsg;
        return $refundResult;
    }

    private function refund($setting, $oid, $rid, $totalPrice, $refundMoney,$merchantMode) {
        $wxPayHelper = new WxPayPubHelper();

        //使用退款接口
        $refund = new Refund_pub();
        $appId = $setting['appId'];
        if($merchantMode){
            $mchId = $setting['merchantMechId'];
            $key = $setting['merchantKey'];
            $refund->setParameter("sub_mch_id",$setting['subMechId']);
        }else{
            $mchId = $setting['mchId'];
            $key = $setting['payKey'];
        }
        $filePem = WXPAY_CERT_PATH . $mchId . ".pem";
        $filePemCert = WXPAY_CERT_PATH . $mchId.  "cert.pem";
        if (!file_exists($filePem) || !file_exists($filePemCert)) {
            Log::write('refund pem not exisit ' . $filePem . ',' . $filePemCert);
            Output::instance()->resultCode(code_product_order_refund_pem_not_exist);
            return;
        }
        //设置必填参数
        //appid已填,商户无需重复填写
        //mch_id已填,商户无需重复填写
        //noncestr已填,商户无需重复填写
        //sign已填,商户无需重复填写
        $refund->setParameter("appid", $appId);
        $refund->setParameter("mch_id", $mchId);
        $refund->setParameter("out_trade_no",$oid);//商户订单号
        $refund->setParameter("out_refund_no", $rid);//商户退款单号
        $refund->setParameter("total_fee",$totalPrice);//总金额
        $refund->setParameter("refund_fee",$refundMoney);//退款金额
        $refund->setParameter("op_user_id",$mchId);//操作员
        //非必填参数，商户可根据实际情况选填
        //$refund->setParameter("sub_mch_id","XXXX");//子商户号
        //$refund->setParameter("device_info","XXXX");//设备号
        //$refund->setParameter("transaction_id","XXXX");//微信订单号

        //调用结果
        $result = $refund->getResult($key, $filePem, $filePemCert);

        //商户根据实际情况设置相应的处理流程,此处仅作举例
        if ($result["return_code"] == "SUCCESS" && $result["result_code"] == "SUCCESS") {
            $refundResult["code"] = 0;
            $refundResult["msg"] = $result["return_msg"];
            return $refundResult;
        } else if ($result["return_code"] == "FAIL") {
            Output::instance()->resultCode(code_product_order_refund_failed, '退款失败服务器无响应');
            Log::write("通信出错：".$result['return_msg']);
            return false;
        }
        else {
            Output::instance()->resultCode(code_product_order_refund_failed, $result["err_code_des"]);
            return false;
        }
    }


    private function createOrder(
        $userName,
        $price,
        $module,
        $moduleId,
        $method,
        $desc = NULL,
        $deliveryType = ORDER_DELIVERYTYPE_DELIVERY,
        $fullName = NULL,
        $phone = NULL,
        $address = NULL,
        $remark = NULL
    ) {
        $customerId = Session::$user['customerId'];
        Session::switchCustomer(0);
        $OrderModel = M("module_product_order");
        $miactivityModel    = M('miactivity');

        // 新建订单的数据
        $createTime = date("Y-m-d H:i:s");
        $data = [
            'fromUserName'      => $userName,
            'price'             => $price,
            'fromCustomerId'    => $customerId,
            'storeId'           => -1,
            'fullName'          => $fullName,
            'phone'             => $phone,
            'address'           => $address,
            'remark'            => $desc,
            'createTime'        => $createTime,
            'updateTime'        => $createTime,
            'type'              => "buy",
            'status'            => order_status_notsubmit,
            'payType'           => ORDER_PAYTYPE_WXPAY,
            'deliveryType'      => $deliveryType,
            'module'            => $module,
            'moduleId'          => $moduleId,
            'method'            => $method
        ];
        // 补充交易类型: 数据库中定义了1-3的用途, 代码(ProduceOrder.class.php)中用到了6, 数据存储中存到了6, 这里新增从7开始(7-报名活动)
        switch ($module) {
            case 'enroll':
                $data['orderType']  = 7;
                break;
            default:
                break;
        }

        Session::switchCustomer($customerId);
        $id = $OrderModel->add($data);
        if ($id) {
            // 订单编号: 客户id + 时间 + id后四位
            $idstr  = strval($id);      // id字符串化
            $length = strlen($idstr);   // id长度
            $idstr = ($length >= 4) ? substr($idstr, -4) : str_pad($idstr, 4, '0', STR_PAD_LEFT);
            $sn = $customerId . date("ymd") . $idstr;
            // 设置上面创建的订单的编号
            $OrderModel->where("id=$id")->setField("serialNumber", $sn);
            $OrderModel->where("id=$id")->setField("tempSerialNumber", $sn);


            /**
             * 这样写是为了兼容以前的代码, 判断哪些功能是需要创建product_order_detail的
             * 同时考虑后期新功能开发
             */
            switch ($module) {
                case 'enroll':
                    // 组织数据
                    $miactivityWhere = ['id' => $moduleId, 'deleted' => 0];
                    $miactivity = $miactivityModel->where($miactivityWhere)->find();
                    $activityJson = json_decode($miactivity['jsonstr'], TRUE);
                    $activityCid  = $miactivity['fromCustomerId'];

                    $Model = new Model();
                    $userWeixinInfoId = $Model->table('wx_' . $activityCid . '_user_wexin_info')->where(['openid' => $userName])->getField('id');
                    // 调用创建
                    $this->createOrderDetail($userName, $id, 0, $activityJson['activityname'], 'buy', $price, 1, $userWeixinInfoId, '');
                    break;
                default:
                    break;
            }

            return $sn;
        } else {
            Log::write("[Wxpay - createOrder] add error:" . $OrderModel->getLastSql());
            return NULL;
        }
    }

    /**
     * @param $fromUserName     购买者openid
     * @param $oid              订单id
     * @param $pid              商品id
     * @param $name             商品名
     * @param $type             类型:order/takeout/buy
     * @param $price            价格
     * @param $count            购买数量
     * @param $userId           购买这id
     * @param $serialNumber     商品编号
     * @return TRUE|FALSE
     */
    private function createOrderDetail($fromUserName, $oid, $pid, $name, $type, $price, $count, $userId, $serialNumber)
    {
        $customerId = Session::$user['customerId'];
        Session::switchCustomer(0);
        $orderDetailModel = M('module_product_order_detail');

        $detailData = [
            'fromUserName'  => $fromUserName,
            'oid'           => $oid,
            'pid'           => $pid,
            'name'          => $name,
            'type'          => $type,
            'price'         => $price,
            'count'         => $count,
            'userId'        => $userId,
            'serialNumber'  => $serialNumber
        ];

        $id = $orderDetailModel->add($detailData);
        if ($id) {
            return TRUE;
        } else {
            Log::write("[Wxpay - createOrderDetail] add error:" . $orderDetailModel->getLastSql());
            return FALSE;
        }
    }

    private function checkSignSubmitPayOld() {
        $wxPayHelper = new WxPayHelper();

        $customerId = Session::$user["customerId"];
        Session::switchCustomer(0);

        $MallSetting = M("customer_mall_setting");
        $setting = $MallSetting->where("customerId=$customerId")->find();
        Session::switchCustomer($customerId);
        if (!$setting) {
            Log::write('checkSignSubmitPay: not set mall setting');
            return true;
        }

        $signType = $_GET['sign_type'];
        if (!empty($signType)) {
            //	$wxPayHelper->setParameter("sign_type", $signType);
// 			Log::write('submit sign type' . $signType);
            if (strcasecmp($signType, 'MD5') !== 0) {
                return true;
            }
        }
        /*
        if (!empty($_GET['service_version'])) {
            $wxPayHelper->setParameter("service_version", $_GET['service_version']);
        }
        if (!empty($_GET['input_charset'])) {
            $wxPayHelper->setParameter("input_charset", $_GET['input_charset']);
        }
        if (!empty($_GET['sign_key_index'])) {
            $wxPayHelper->setParameter("sign_key_index", $_GET['sign_key_index']);
        }
        if (!empty($_GET['pay_info'])) {
            $wxPayHelper->setParameter("pay_info", $_GET['pay_info']);
        }
        if (!empty($_GET['bank_billno'])) {
            $wxPayHelper->setParameter("bank_billno", $_GET['bank_billno']);
        }
        if (!empty($_GET['attach'])) {
            $wxPayHelper->setParameter("attach", $_GET['attach']);
        }
        if (!empty($_GET['transport_fee'])) {
            $wxPayHelper->setParameter("transport_fee", $_GET['transport_fee']);
        }
        if (!empty($_GET['product_fee'])) {
            $wxPayHelper->setParameter("product_fee", $_GET['product_fee']);
        }
        if (!empty($_GET['discount'])) {
            $wxPayHelper->setParameter("discount", $_GET['discount']);
        }
        if (!empty($_GET['buyer_alias'])) {
            $wxPayHelper->setParameter("buyer_alias", $_GET['buyer_alias']);
        }
        $wxPayHelper->setParameter("out_trade_no", $_GET['out_trade_no']);
        $wxPayHelper->setParameter("trade_mode", $_GET['trade_mode']);
        $wxPayHelper->setParameter("trade_state", $_GET['trade_state']);
        $wxPayHelper->setParameter("partner", $_GET['partner']);
        $wxPayHelper->setParameter("bank_type", $_GET['bank_type']);
        $wxPayHelper->setParameter("total_fee", $_GET['total_fee']);
        $wxPayHelper->setParameter("fee_type", $_GET['fee_type']);
        $wxPayHelper->setParameter("notify_id", $_GET['notify_id']);
        $wxPayHelper->setParameter("transaction_id", $_GET['transaction_id']);
        $wxPayHelper->setParameter("time_end", $_GET['time_end']);
        */
        $para_filter = array();
        foreach ($_GET as $key => $val ) {
            if($key == "sign_method" || $key == "sign" ||$val == "") {
                continue;
            } else {
                $wxPayHelper->setParameter($key, $_GET[$key]);
            }
        }

        return $wxPayHelper->checkSignMd5($_GET['sign'], $setting["partnerKey"]);
    }

    private function checkSignFeedBack($appId, $timeStamp, $openId, $sign) {
        $customerId = Session::$user["customerId"];
        Session::switchCustomer(0);
        $MallSetting = M("customer_mall_setting");
        $setting = $MallSetting->where("customerId=$customerId")->find();
        Session::switchCustomer($customerId);
        if (!$setting) {
            return;
        }

        $wxPayHelper = new WxPayHelper();
        $obj["appId"] = $appId;
        $obj["timpstamp"] = $timeStamp;
        $obj["openId"] = $openId;
        $tempSign =  $wxPayHelper->get_biz_sign($obj, $setting["paySignKey"]);
        if ($tempSign == $sign) {
            return true;
        } else {
            Log::write('feed back sign:'.$tempSign . ',should be:' . $sign);
            return false;
        }
    }

    private static function sendNoticeWarning($tile, $desc) {
        $isPush = Module::callModule(module_handle_pushMsg, NULL, 'isPush', NULL);
        if ($isPush) {
            $articles = array();
            $article = new stdClass();
            $article->title = $title;
            $article->description = $desc;
            $article->picUrl = '';
            $article->url = '';
            array_push($articles, $article);

            $params = new stdClass();
            $params->type = module_handle_news;
            $params->title = '';
            $params->msg = $articles;
            Module::callModule(module_handle_pushMsg, NULL, 'pushMsgToBind', $params);
        }
    }
    /**
    /**
     * 获取微信公钥
     * @param string $partnerkey       商户自带签名
     * @param string $mch_id            商户id
     * @param string $nonce_str         随机字符串
     * @return bool|SimpleXMLElement[]
     */
    public function getpublickey(){
        $setting = R("CUser/getMallSetting");
        $nonce_str = self::createNonceStr(32);
        $mch_id = $arr['mch_id']= $setting['mchId'];
        $arr['nonce_str'] = $nonce_str;
        $arr['sign_type']='MD5';
        $wxPayHelper = new WxPayHelper();
        if(empty($setting['payKey'])){
            $setting['payKey'] =  C("PLANT_PARTNERKEY");
        }
        $xmls=$wxPayHelper->getSignValBykeyArr($setting['payKey'],$arr);
        $url='https://fraud.mch.weixin.qq.com/risk/getpublickey';
        $second=30;
        $aHeader=array();
        $ch = curl_init();
        //超时时间
        curl_setopt($ch,CURLOPT_TIMEOUT,$second);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
        //这里设置代理，如果有的话
        curl_setopt($ch,CURLOPT_URL,$url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,false);
        //cert 与 key 分别属于两个.pem文件
        curl_setopt($ch,CURLOPT_SSLCERT,WXPAY_CERT_PATH.$mch_id.'cert.pem');
        curl_setopt($ch,CURLOPT_SSLKEY,WXPAY_CERT_PATH.$mch_id.'.pem');
        if( count($aHeader) >= 1 ){
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
        }
        curl_setopt($ch,CURLOPT_POST, 1);
        curl_setopt($ch,CURLOPT_POSTFIELDS,$xmls);
        $data = curl_exec($ch);
        curl_close ( $ch );
        $responseObj = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);

        if($responseObj->result_code == 'SUCCESS'){
            file_put_contents(WXPAY_CERT_PATH.$mch_id.'newpublic.pem',$responseObj->pub_key);
            echo $responseObj->pub_key;
        }else{
            log::write('LPdayin：Errno：' . curl_error ( $ch )) ;
        }
    }


    //为获取签名而写
    public static function createNonceStr($length = 16)
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 公众号付款到银行卡
     * @param string $partnerkey    自带签名key
     * @param $mch_id               商户id
     * @param $amount               金额
     * @param $enc_bank_no          银行卡号
     * @param $enc_true_name        银行账户名
     * @param $bank_code            银行编号
     * @param string $desc          备注
     * @return bool|SimpleXMLElement[]
     */
    public static function pay_bank($partnerkey = '', $mch_id, $amount, $enc_bank_no, $enc_true_name, $bank_code, $desc = '')
    {
        if ($partnerkey == '') {
            $partnerkey = C("PLANT_PARTNERKEY");
        }
        $Rsa = new Rsa(file_get_contents(WXPAY_CERT_PATH .$mch_id. 'public.pem'));;
        $enc_bank_no = $Rsa->public_encrypt($enc_bank_no);
        $enc_true_name = $Rsa->public_encrypt($enc_true_name);

        $amount = ceil($amount * 100);
        $arr['mch_id'] = $mch_id;
        $arr['partner_trade_no'] = $arr['mch_id'] . date('YmdHis') . rand(1000, 9999);
        $arr['nonce_str'] = self::createNonceStr(32);
        $arr['enc_bank_no'] = $enc_bank_no;
        $arr['enc_true_name'] = $enc_true_name;
        $arr['bank_code'] = $bank_code;
        $arr['amount'] = $amount;
        $arr['desc'] = $desc;
        $wxPayHelper = new WxPayHelper();
        $xmls = $wxPayHelper->getSignValBykeyArr($partnerkey, $arr);
        $url = 'https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank';
        $second = 30;
        $aHeader = array();
        $ch = curl_init();
        //超时时间
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //这里设置代理，如果有的话
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        //cert 与 key 分别属于两个.pem文件
        curl_setopt($ch, CURLOPT_SSLCERT, WXPAY_CERT_PATH . $mch_id . 'cert.pem');
        curl_setopt($ch, CURLOPT_SSLKEY, WXPAY_CERT_PATH . $mch_id . '.pem');
        if (count($aHeader) >= 1) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
        }
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xmls);
        $data = curl_exec($ch);
        curl_close($ch);
        $responseObj = simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
        if (curl_errno ( $ch )) {
            log::write('LPdayin：Errno：' . curl_error ( $ch )) ;
        }
        return $responseObj;
    }

}